using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using Data;
using Data.Avl;
using Data.Probabilities;
using Logic.IB;

namespace Logic.Agglomerative
{
    class AgglomerativeData: IAgglomerative
    {
        //delete this row
        ArrayList colAvlList;
        ProbabilityData ProbData;
        
        #region Constructors


        public AgglomerativeData(ProbabilityData newProbData)
        {
            this.ProbData = newProbData;
            colAvlList = new ArrayList();
            for (int i = 0; i < ProbData.NumberOfGenomes; i++)
            {
                colAvlList.Add(new AVLTree<Node>());
            }
            createDist(); //need to be in the final code
        }

        #endregion

        #region Methods

        private void initializeTree()
        {
            AVLTree<Node> tree;
            ArrayList treeArr = new ArrayList();

            //DistanceFunction Distances = new DistanceFunction(ProbData);
            for (int j = 0; j < ProbData.NumberOfGenomes; j++)
            {
                tree = new AVLTree<Node>();
                for (int i = 0; i < ProbData.NumberOfGenomes; i++)
                {
                    if (i != j)
                    {
                        tree.Add(new Node(j, i, DistanceFunction.getDistance(i, j)));
                    }
                }
                treeArr.Add(tree);
            }
            colAvlList = treeArr;
        }


        /// <summary>
        /// 
        /// </summary>
        private void createDist()
        {
            //DistanceFunction Distances=new DistanceFunction(ProbData);
            for (int i = 0; i < ProbData.NumberOfGenomes; i++)
            {
                for (int j = i+1; j < ProbData.NumberOfGenomes; j++)
                {
                    double distI2J = DistanceFunction.getDistance(i, j);
                    ((AVLTree<Node>)colAvlList[i]).Add(new Node(j, i, distI2J));
                    ((AVLTree<Node>)colAvlList[j]).Add(new Node(i, j, distI2J));
                }
            }
        }

        #endregion

        #region Ihierarchy Members

        /// <summary>
        /// go over all the trees and find the minimal value.
        /// </summary>
        /// <returns>Node of the AVL tree with the minimal Value of the Key Feild</returns>
        public Node getMin()
        {
            ArrayList minValues = new ArrayList();
            Node curMinVal = null;
            foreach (AVLTree<Node> tree in colAvlList)
            {
                if ((tree != null) && (tree.Root != null))
                    minValues.Add(tree.findMin().Value);
            }
            foreach (Node optMin in minValues)
            {
                if (curMinVal == null)
                    curMinVal = optMin;
                else
                {
                    if (curMinVal.Key > optMin.Key)
                        curMinVal = optMin;
                }
            }
            return curMinVal;
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="first"></param>
        /// <param name="second"></param>
        /// <param name="typeOfMerge"></param>
        /// <param name="stepNum"></param>
        /// <returns></returns>
        public void merge(int first, int second, int typeOfMerge, int stepNum)
        {
            Node newNode;
            AVLTree<Node> resultAvlTree = new AVLTree<Node>();
            /* Creat arrays from the trees */
            ArrayList FirstTree = new ArrayList();
            ArrayList SecondTree = new ArrayList();
            //make from this two trees an arraylist of them nodes
            ((AVLTree<Node>)colAvlList[first]).CopyToA(FirstTree, 0);
            ((AVLTree<Node>)colAvlList[second]).CopyToA(SecondTree, 0);
            /* sort the arrays by the row index */
            Node.sortyByIndexI f = new Node.sortyByIndexI();
            //we want the nodes to be in the same indexes in boths arrays so the merge step will be easyer
            FirstTree.Add(new Node(first, first, 0));
            SecondTree.Add(new Node(second, second, 0));
            //sort the arrays by the i index of the node(so we now merge each one from the first to the
            //correct Node in the second array
            FirstTree.Sort(f);
            SecondTree.Sort(f);
            /* merge the arrays into Avltree with the new data */
            foreach (Node i in FirstTree)
            {
                if (i.Key != 0)
                {
                    Node temp;
                    //we are in the case that we need to merge the distances that are going to be 0
                    //in this case we dont need to insert this node to the new tree so we only remove the 
                    //old values from the others trees
                    if (i.I != second)
                    {
                        //get the other cell we need to merge with the curent cell
                        temp = (Node)SecondTree[FirstTree.IndexOf(i)];
                        //merge the two nodes with the type of merge we get as parameter
                        newNode = MergeFunction.merge(i, temp, typeOfMerge, stepNum);
                        //add the new node to the new tree
                        resultAvlTree.Add(newNode);
                        //remove the old one from its tree
                        ((AVLTree<Node>)colAvlList[i.I]).Remove(new Node(i.J, i.I, i.Key));
                        //remove the nodes that are in the second array from all others trees
                        ((AVLTree<Node>)colAvlList[i.I]).Remove(new Node(temp.J, temp.I, temp.Key));
                        //add the new node (with the correct indexes switch between i and j)
                        ((AVLTree<Node>)colAvlList[newNode.I]).Add(new Node(newNode.J, newNode.I, newNode.Key));//insert the new node to the others trees
                    }
                }
            }
            colAvlList.Add(resultAvlTree);
            colAvlList[first] = null;
            colAvlList[second] = null;
        }

        //public void testTree(int first, int second, int father)
        //{
        //    ArrayList helpMe = new ArrayList();
        //    bool Flag = false;
        //    foreach (AVLTree<Node> tree in colAvlList)
        //    {
        //        if (tree.Root != null)
        //        {
        //            tree.CopyToA(helpMe, 0);
        //            Flag = false;
        //            foreach (Node nodee in helpMe)
        //            {

        //                if (nodee.I == first)
        //                    throw new Exception("didnt remove the" + first.ToString() + "row in column" + nodee.J.ToString());
        //                if (nodee.I == second)
        //                    throw new Exception("didnt remove the" + second.ToString() + "row in column" + nodee.J.ToString());
        //                if (nodee.I == father)
        //                    Flag = true;
        //            }
        //            if (!Flag)
        //                throw new Exception("didnt add the" + father.ToString() + "to column Tree" + tree.Root.Value.J.ToString());
        //        }
        //    }
        //}

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public int MaxNumOfIteration()
        {
            return colAvlList.Count;
        }

        #endregion


        #region IAgglomerative Members

        public int ColAvlList
        {
            set { colAvlList[value] = null; }
        }

        #endregion
    }
}
